package com.jiutianniao.p2p.acctrans.core.facade.impl;

import java.util.List;

import javax.annotation.Resource;
import javax.jws.WebService;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.jiutianniao.p2p.acctrans.core.constants.AccountTransConst;
import com.jiutianniao.p2p.acctrans.core.exception.TransException;
import com.jiutianniao.p2p.acctrans.core.trans.DepositTrans;
import com.jiutianniao.p2p.acctrans.core.trans.DoubleAccountTrans;
import com.jiutianniao.p2p.acctrans.core.trans.FreezeAccountBalanceTrans;
import com.jiutianniao.p2p.acctrans.core.trans.UnFreezeAccountBalanceTrans;
import com.jiutianniao.p2p.acctrans.core.trans.WithdrawTrans;
import com.jiutianniao.p2p.base.model.Money;
import com.jiutianniao.p2p.trans.client.enums.SubTransCodeEnum;
import com.jiutianniao.p2p.trans.client.enums.TransCodeEnum;
import com.jiutianniao.p2p.trans.client.enums.TransResultEnum;
import com.jiutianniao.p2p.trans.client.facade.AccountTransFacade;
import com.jiutianniao.p2p.trans.client.model.TransInfo;
import com.jiutianniao.p2p.trans.client.result.AccountTransResult;

@WebService(endpointInterface = "com.jiutianniao.p2p.trans.client.facade.AccountTransFacade", serviceName = "AccountTransFacade")
@Service("accountTransFacade")
public class AccountTransFacadeImpl implements AccountTransFacade {

	private Logger logger = Logger.getLogger(this.getClass());
	@Resource
	private DepositTrans depositTrans;

	@Resource
	private WithdrawTrans withdrawTrans;

	@Resource
	private FreezeAccountBalanceTrans freezeAccountBalanceTrans;

	@Resource
	private PlatformTransactionManager transactionManager;

	@Resource
	private UnFreezeAccountBalanceTrans unFreezeAccountBalanceTrans;

	@Resource
	private DoubleAccountTrans doubleAccountTrans;

	@Override
	public AccountTransResult doWithdraw(TransInfo transInfo) {
		AccountTransResult result = new AccountTransResult();
		try {

			transInfo.setTransCode(TransCodeEnum.TXCODE_APPLY_WITHDRAW);

			Money amount = transInfo.getTransAmount();
			transInfo.setTransAmount(amount);

			String depositAccountNo = transInfo.getTransAccountIn();
			transInfo.setAccountInType(getAccountType(depositAccountNo));

			String creditAccountNo = transInfo.getTransAccountOut();
			transInfo.setAccountOutType(getAccountType(creditAccountNo));

			withdrawTrans.doTransaction(transInfo);
			result.setSuccess(true);
		} catch (TransException e) {
			e.printStackTrace();
			result.setResultCode(e.getCode().name());
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.setResultCode(TransResultEnum.common_error.name());
			return result;
		}

		return result;
	}

	@Override
	public AccountTransResult doFreeze(TransInfo transInfo) {
		AccountTransResult result = new AccountTransResult();

		transInfo.setTransCode(TransCodeEnum.TXCODE_FREEZE_ACCOUNT);

		Money freezeMoney = transInfo.getTransAmount();
		transInfo.setTransAccountOut(null);

		String accountNo = transInfo.getTransAccountIn();

		transInfo.setAccountOutType(getAccountType(accountNo));

		String voucherId = transInfo.getVoucherId();

		transInfo.setVoucherId(voucherId);

		try {
			freezeAccountBalanceTrans.doTransaction(transInfo);
			result.setSuccess(true);
		} catch (TransException e) {
			e.printStackTrace();
			result.setResultCode(e.getCode().name());
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.setResultCode(TransResultEnum.common_error.name());
			return result;
		}

		return result;
	}

	@Override
	public AccountTransResult doUnFreeze(TransInfo transInfo) {
		AccountTransResult result = new AccountTransResult();

		transInfo.setTransCode(TransCodeEnum.TXCODE_UN_FREEZE_ACCOUNT);

		String accountNo = transInfo.getTransAccountOut();
		transInfo.setAccountInType(getAccountType(accountNo));

		transInfo.setTransAccountIn(null);

		String voucherId = transInfo.getVoucherId();

		transInfo.setVoucherId(voucherId);

		try {
			unFreezeAccountBalanceTrans.doTransaction(transInfo);

			result.setSuccess(true);
		} catch (TransException e) {
			e.printStackTrace();
			result.setResultCode(e.getCode().name());
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.setResultCode(TransResultEnum.common_error.name());
			return result;
		}

		return result;
	}

	@Override
	public AccountTransResult doUnFreezeAndWithdraw(final TransInfo transInfo) {
		AccountTransResult result = new AccountTransResult();
		TransactionTemplate transactionTemplate = new TransactionTemplate(
				transactionManager);
		transactionTemplate
				.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
		try {

			final TransInfo transInfo2 = transInfo.clone();

			transactionTemplate.execute(new TransactionCallbackWithoutResult() {
				@Override
				protected void doInTransactionWithoutResult(
						TransactionStatus status) {
					AccountTransResult result = doUnFreeze(transInfo2);
					if (result.isSuccess()) {
						AccountTransResult withdrawResult = doWithdraw(transInfo);
						if (withdrawResult.isSuccess()) {
							return;
						}
					}
					status.setRollbackOnly();
				}
			});
			result.setSuccess(true);
		} catch (TransException e) {
			e.printStackTrace();
			result.setResultCode(e.getCode().name());
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.setResultCode(TransResultEnum.common_error.name());
			return result;
		}

		return result;
	}

	private boolean checkSubCode(TransCodeEnum transCode,
			SubTransCodeEnum subTransCode) {
		if (subTransCode.getCode().contains(
				String.valueOf(TransCodeEnum.TXCODE_TRANSFER.getCode()))) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public AccountTransResult transfer(final TransInfo transInfo) {
		AccountTransResult result = new AccountTransResult();
		{

			TransCodeEnum transCode = transInfo.getTransCode();
			if (transCode == null) {
				transCode = TransCodeEnum.TXCODE_TRANSFER;
				transInfo.setTransCode(transCode);
			}

			SubTransCodeEnum subTransCode = transInfo.getSubTransCode();
			if (subTransCode != null) {
				boolean isSub = checkSubCode(transCode, subTransCode);
				if (!isSub) {
					result.setResultCode(TransResultEnum.trans_code_error
							.name());
					return result;
				}
			}

			String creditAccountNo = transInfo.getTransAccountIn();
			if (StringUtils.isBlank(creditAccountNo)) {// 空账号则默认补全为中间账号
				creditAccountNo = AccountTransConst.MIDDLE_ACCOUNT_NO;
				transInfo.setTransAccountIn(creditAccountNo);
			}
			transInfo.setAccountInType(getAccountType(creditAccountNo));

			String dipositAccountNo = transInfo.getTransAccountOut();
			if (StringUtils.isBlank(dipositAccountNo)) {// 空账号则默认补全为中间账号
				dipositAccountNo = AccountTransConst.MIDDLE_ACCOUNT_NO;
				transInfo.setTransAccountOut(dipositAccountNo);
			}
			transInfo.setAccountOutType(getAccountType(dipositAccountNo));

			String voucherId = transInfo.getVoucherId();

			transInfo.setVoucherId(voucherId);
		}

		try {
			doubleAccountTrans.doTransaction(transInfo);
			result.setSuccess(true);
		} catch (TransException e) {
			e.printStackTrace();
			result.setResultCode(e.getCode().name());
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.setResultCode(TransResultEnum.common_error.name());
			return result;
		}

		return result;
	}

	@Override
	public AccountTransResult doDeposit(TransInfo transInfo) {
		AccountTransResult result = new AccountTransResult();
		{

			transInfo.setTransCode(TransCodeEnum.TXCODE_DEPOSIT);

			String depositAccountNo = transInfo.getTransAccountIn();
			transInfo.setAccountInType(getAccountType(depositAccountNo));

			String creditAccountNo = transInfo.getTransAccountOut();
			transInfo.setAccountOutType(getAccountType(creditAccountNo));

			String voucherId = transInfo.getVoucherId();

			transInfo.setVoucherId(voucherId);
		}

		try {
			AccountTransResult result2 = depositTrans.doTransaction(transInfo);
			return result2;
		} catch (Exception e) {
			logger.error("depositTrans.doTransaction", e);
			return result;
		}

	}

	@Override
	public AccountTransResult doUnFreezeAndTransfer(final TransInfo transInfo) {
		AccountTransResult result = new AccountTransResult();
		TransactionTemplate transactionTemplate = new TransactionTemplate(
				transactionManager);
		transactionTemplate
				.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
		try {

			final TransInfo transInfo2 = transInfo.clone();

			transactionTemplate.execute(new TransactionCallbackWithoutResult() {
				@Override
				protected void doInTransactionWithoutResult(
						TransactionStatus status) {

					transInfo2.setSubTransCode(null);
					AccountTransResult result = doUnFreeze(transInfo2);
					if (result.isSuccess()) {
						AccountTransResult withdrawResult = transfer(transInfo);
						if(withdrawResult.isSuccess()){
							return;
						}
					}
					status.setRollbackOnly();
					throw new RuntimeException(result.getResultCode());
				}
			});
			result.setSuccess(true);
		} catch (TransException e) {
			logger.error("doInTransactionWithoutResult", e);
			result.setResultCode(e.getCode().name());
			return result;
		} catch (Exception e) {
			logger.error("doInTransactionWithoutResult", e);
			e.printStackTrace();
			result.setResultCode(TransResultEnum.common_error.name());
			return result;
		}

		return result;
	}

	@Override
	public AccountTransResult batchTransfer(final List<TransInfo> transList) {
		AccountTransResult result = new AccountTransResult();
		TransactionTemplate transactionTemplate = new TransactionTemplate(
				transactionManager);
		transactionTemplate
				.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
		try {

			transactionTemplate.execute(new TransactionCallbackWithoutResult() {
				@Override
				protected void doInTransactionWithoutResult(
						TransactionStatus status) {
					for (TransInfo transInfo : transList) {
						TransCodeEnum codeEnum = transInfo.getTransCode();
						if (codeEnum != TransCodeEnum.TXCODE_TRANSFER) {
							throw new TransException(
									TransResultEnum.trans_code_error);
						}
						AccountTransResult transResult = doubleAccountTrans
								.doTransaction(transInfo);
						if (!transResult.isSuccess()) {
							status.setRollbackOnly();
							throw new TransException(
									TransResultEnum.common_error);
						}
					}

				}
			});
			result.setSuccess(true);
		} catch (TransException e) {
			logger.error("batchTransfer", e);
			result.setResultCode(e.getCode().name());
			return result;
		} catch (Exception e) {
			logger.error("batchTransfer", e);
			result.setResultCode(TransResultEnum.common_error.name());
			return result;
		}
		return result;
	}

	private int getAccountType(String accountNo) {
		return Integer.valueOf(accountNo.substring(5, 6));
	}
}
